Related to:
- Fig 3c-h
- Extended Data Fig 6b,c
- Extended Data Fig 7f
suppressPackageStartupMessages({
library(Seurat)
library(SeuratObject)
library(SummarizedExperiment)
library(ggplot2)
library(ggpubr)
library(tidyr)
library(patchwork)
library(viridis)
})
SAVEFIGS <- FALSE
TBO_seurat<-readRDS("../data/05_2025_RPM_RPMA_TBO_CellTag_Seurat_wSigs_FA_dpt_final.rds")
TBO_seurat
An object of class Seurat
110982 features across 26618 samples within 2 assays
Active assay: RNA (55491 features, 0 variable features)
2 layers present: counts, data
1 other assay present: norm
2 dimensional reductions calculated: umap, fa
clones <- readRDS("../data/05_2025_RPM_RPMA_TBOAllo_CellTagClones_Onlyclones.rds")
my_colors <- c(
"#E41A1C", # strong red
"#377EB8", # medium blue
"#4DAF4A", # green
"#984EA3", # purple
"#FF7F00", # orange
"#FFFF33", # yellow
"#A65628", # brown
"#e7298a", # pink
"#666666", # grey
"#66C2A5", # teal
"#FC8D62", # salmon
"#8DA0CB", # soft blue
"#E78AC3", # soft pink (different from 8)
"#A6D854", # light green (but yellowish tint, not green)
"#FFD92F", # lemon yellow
"#E5C494", # light brown
"#B3B3B3", # light grey
"#1B9E77", # deep teal
"#D95F02", # dark orange
"#7570B3", # strong purple
"#66A61E" # olive green (NOT same green as before)
)
pheno_col <- c("brown2","darkorchid4","dodgerblue","#66A61E","orange","turquoise4","turquoise")
names(pheno_col) <- c("NE","NE/Neuronal","Neuronal","ATOH1","Tuft","Triple-Neg","Basal")
Fig 3c
ForceAtlas embedding of RPM and RPMA CellTagged allografted tumor
cells
DimPlot(TBO_seurat, group.by='leiden_scVI_1.2', cols=my_colors, reduction='fa', label=TRUE, label.size=4) &
NoAxes() + theme(legend.position="none") # suppress legend

DimPlot(TBO_seurat, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6) &
NoAxes()

# Extract coordinates from force-directed layout (or any layout)
dat <- Embeddings(TBO_seurat, reduction = "fa") %>%
as.data.frame() %>%
mutate(Cluster = TBO_seurat$leiden_scVI_1.2,
Genotype = TBO_seurat$Genotype)
# Set up your color vector (named)
my_colors_named <- setNames(my_colors, levels(TBO_seurat$leiden_scVI_1.2))
# Plot each Genotype with background cells greyed
plots <- lapply(unique(dat$Genotype), function(g) {
dat$highlight <- ifelse(dat$Genotype == g, "highlight", "background")
dat$Color <- ifelse(dat$Genotype == g, my_colors_named[dat$Cluster], "lightgrey")
ggplot(dat, aes(x = FA_1, y = FA_2)) +
geom_point(aes(color = Color), size = 0.5) +
scale_color_identity() +
ggtitle(g) +
theme_void() +
theme(
plot.title = element_text(hjust = 0.5),
legend.position = "none",
plot.background = element_rect(fill = "transparent", color = NA)
)
})
# Combine into one figure
a <- wrap_plots(plots, ncol = 2)
a

if(SAVEFIGS) ggsave("FAbyGeno.png", plot= a, width=6, height=3, dpi=300, bg = "transparent")
Visualizing Celltag/clone data
From table above, added whether clones were robust or not
Robust defined as >5 cells per clone post-QC.
CellTag metadata: clone information can also be found in Supplementary
Table 4 of Ireland et al, 2025
Idents(TBO_seurat) <- 'Robust'
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$UnID)
RPMA_Allo RPM_Allo3 RPM_Allo4 RPM_Allo_New
No 6659 5244 6523 2227
Robust 3501 1474 337 653
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$GenoCT)
RPMA_CTpreCre RPM_CTpostCre RPM_CTpreCre
No 6659 11767 2227
Robust 3501 1811 653
Identify robust clones
table(clones@meta.data$Robust, clones@meta.data$Genotype)
RPM RPMA
Robust 2464 3501
Assess clones by leiden cluster
Fig. 3d
First, just look at bar graph, no particular order #
Idents(clones) <- 'leiden_scVI_1.2'
x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))
# proportions$Cluster
colnames(proportions)<-c("Cluster", "Sample", "Frequency")
# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) +
geom_bar(position="stack", stat="identity")
p + scale_fill_manual(values=my_colors) +
theme_bw()+ theme(axis.text.y = element_text(size=20),
axis.text.x=element_text(size=14), axis.title.x =element_text(size=14),
axis.title.y = element_text(size=18), legend.text = element_text(size=12),
legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90)

dat <- p$data
Transform data to perform hierarchical (agglomerative) clustering
# Pivot to wide format: Cluster × Sample
mat <- dat %>%
pivot_wider(names_from = Sample, values_from = Frequency, values_fill = 0) %>%
tibble::column_to_rownames("Cluster") %>%
as.matrix()
mat_norm <- prop.table(mat, margin = 1) # normalize each row to sum to 1
Hierarchical (agglomerative) clustering of clones with default
parameters in pheatmap()
hm <- pheatmap::pheatmap(t(mat), cutree_rows = 1, cutree_cols = 8, cellwidth = 5,
cellheight = 5, fontsize = 8,
cluster_rows=FALSE, border_color=NA,
color = colorRampPalette(c("darkturquoise","black","red2"))(30))

clone_order <- colnames(t(mat))[hm$tree_col$order]
Fig 3d
Clustered clones by proportions of cells in Leiden clusters
# Now, plot clones by Leiden, ordered, final # (Fig. 3d)
Idents(clones)<-'leiden_scVI_1.2'
x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))
colnames(proportions) <- c("Cluster", "Sample", "Frequency")
proportions$Cluster <- factor(proportions$Cluster, clone_order)
# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) +
geom_bar(position="stack", stat="identity")
p + scale_fill_manual(values=my_colors) +
theme_bw() + theme(axis.text.y = element_text(size=20),
axis.text.x=element_text(size=14), axis.title.x =element_text(size=14),
axis.title.y = element_text(size=18), legend.text = element_text(size=12),
legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90) +
labs(x = NULL, y = "% of clone")

Idents(clones) <- 'Clone_Dynamics'
table(clones@meta.data$Clone_Dynamics)
Pattern_1 Pattern_2 Pattern_3 Pattern_4 Pattern_5 Unknown_1 Unknown_2
553 675 693 2797 1032 167 48
Function for plotting clone dynamics
clone_patterns <- c('Pattern_1', 'Pattern_2', 'Pattern_3', 'Pattern_4', 'Pattern_5', 'Unknown_1', 'Unknown_2')
pattern_colors <- c('orange', 'green2', 'red', 'royalblue2', 'purple', 'gray40', 'black')
names(pattern_colors) <- clone_patterns
plotCloneDyn <- function(clone_pattern, pattern_color, tit = "") {
# Get cells of interest
ss <- subset(clones, idents = clone_pattern)
highlighted_cells <- rownames(ss@meta.data)
# Plot all cells in grey, highlight the pattern in your chosen color
DimPlot(clones,
reduction = "fa",
cells.highlight = highlighted_cells,
sizes.highlight = 0.25,
cols.highlight = pattern_color,
cols = "grey90",
pt.size = 0.1) +
ggtitle(tit) & NoLegend() & NoAxes()
}
Fig 3e
ForceAtlas embedding showing representative clones of each pattern
with cells colored by pattern
cdp <- (lapply(1:7, function(i) plotCloneDyn(clone_patterns[i], pattern_colors[i], tit=clone_patterns[i])))
p_combined <- ggarrange(plotlist = cdp, ncol = 4, nrow = 2)
p_combined

getCloneCellIDs <- function(id) {
cellIDs <- rownames(subset(clones,idents=c(id))@meta.data)
}
clone_cell_ids <- lapply(setNames(clone_patterns, clone_patterns), getCloneCellIDs)
plotPhenoWithBackground <- function(clone_pattern, pheno_col, title = "", seurat_obj = clones) {
# Check if 'fa' reduction exists
if (!"fa" %in% names(seurat_obj@reductions)) {
stop("The 'fa' reduction is not found in the Seurat object.")
}
# Extract FA coordinates and metadata
coords <- as.data.frame(Embeddings(seurat_obj, reduction = "fa"))
coords$cell <- rownames(coords)
# Get metadata
meta <- seurat_obj@meta.data[, c("Clone_Dynamics", "Pheno")]
meta$cell <- rownames(meta)
# Merge coordinates and metadata
dat <- merge(coords, meta, by = "cell")
# Flag highlighted cells
dat$highlight <- dat$Clone_Dynamics == clone_pattern
dat$Pheno <- droplevels(dat$Pheno)
# Split background and foreground
bg_dat <- dat[!dat$highlight, ]
fg_dat <- dat[dat$highlight, ]
if (nrow(fg_dat) == 0) {
warning(paste("No cells found for Clone_Dynamics pattern:", clone_pattern))
return(ggplot() + ggtitle(paste(clone_pattern, "(no cells)")))
}
# Plot
p <- ggplot() +
geom_point(data = bg_dat, aes(x = FA_1, y = FA_2), color = "gray90", size = 0.2) +
geom_point(data = fg_dat, aes(x = FA_1, y = FA_2, color = Pheno), size = 1) +
scale_color_manual(values = pheno_col, drop = FALSE) +
ggtitle(title) +
theme_void() +
theme(
legend.position = "none",
panel.background = element_rect(fill = "transparent", color = NA),
plot.background = element_rect(fill = "transparent", color = NA)
)
return(p)
}
ForceAtlas embedding showing cells colored by SCLC phenotype
DimPlot(clones, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6, pt.size = 0.05) &
NoAxes()

Fig 3f
ForceAtlas embedding showing representative clones of each pattern
with cells colored by SCLC phenotype
plots <- lapply(clone_patterns, function(pat) plotPhenoWithBackground(pat, pheno_col, title = pat))
p_combined <- ggarrange(plotlist = plots, ncol = 4, nrow = 2)
p_combined

Visualize individual clones
Ext Data Fig. 6b,c
plotPatternDynByClone <- function(clone_pattern) {
Idents(clones)<-'Clone_Dynamics'
p1 <- subset(clones,idents=c(clone_pattern))
pattern_color <- pattern_colors[clone_pattern]
test <- as.data.frame(p1$CellTag_Clone)
test$Barcodes <- rownames(test)
# Group by CellTag_Clone
test <- test %>% group_by(p1$CellTag_Clone)
test <- dplyr::group_split(test)
n_clones <- length(test)
# Plot in for loop all RPM clones in Pattern 1
plot_lst <- vector("list", length = n_clones)
for (i in seq(n_clones)) {
g <- DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', order=TRUE,
cells.highlight=test[[i]]$Barcodes,sizes.highlight=2,
cols.highlight=pattern_color) +
ggtitle(paste0(test[[i]]$`p1$CellTag_Clone`[1])) +
theme(plot.title = element_text(size = 8,face = "plain")) & NoLegend() &
NoAxes()
plot_lst[[i]] <- g
}
# Combine multiple plots for output, as desired
return(cowplot::plot_grid(plotlist = plot_lst, ncol=5))
}
pattern_plot_list <- lapply(clone_patterns, plotPatternDynByClone)
n_plots_per_pattern <- sapply(pattern_plot_list, function(x) length(x[['layers']]))
## calculate height of figure based on number of plots per pattern
plt_height <- 2.667 * ((n_plots_per_pattern %/% 5) + 1)
Ext Data Fig 6b,c
pattern_plot_list[[1]]

pattern_plot_list[[2]]

pattern_plot_list[[3]]

pattern_plot_list[[4]]

pattern_plot_list[[5]]

pattern_plot_list[[6]]

pattern_plot_list[[7]]

Visualize only clones matching in vivo in FA projection
Ext Data Fig. 7f
Idents(clones) <- "CellTag_Clone"
# Subset just the clones that match the in vitro, pattern 1
invitromatch <- subset(clones, idents=c("RPM_Clone_14","RPM_Clone_2","RPM_Clone_33","RPM_Clone_36","RPM_Clone_6"))
p1_cells <- colnames(invitromatch)
p2_cells <- colnames(subset(clones, idents="RPM_Clone_23"))
p5_cells <- colnames(subset(clones, idents="RPM_Clone_13"))
DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p1_cells, sizes.highlight=2,
cols.highlight=c("orange")) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p2_cells, sizes.highlight=2,
cols.highlight=pattern_colors["Pattern_2"]) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p5_cells, sizes.highlight=2,
cols.highlight=pattern_colors["Pattern_5"]) + ggtitle("") & NoLegend() & NoAxes()

NA
NA
Fig. 3g
dpt psuedotime in FA space
FeaturePlot(TBO_seurat, features = c("dpt_pseudotime"), pt.size=0.01,
reduction='fa',) + scale_color_viridis(option="viridis",direction=-1)& NoAxes()

Function to plot clone dynamics colored by DPT
plotCloneDynDpt <- function(clone_pattern, seurat_obj, title = "") {
# Subset cells of interest
ss <- subset(seurat_obj, idents = clone_pattern)
highlight_cells <- colnames(ss)
# Extract embeddings and metadata
fa_coords <- Embeddings(seurat_obj, "fa")
dpt_vals <- seurat_obj@meta.data$dpt_pseudotime
names(dpt_vals) <- rownames(seurat_obj@meta.data)
dat <- as.data.frame(fa_coords)
dat$highlight <- ifelse(rownames(dat) %in% highlight_cells, "yes", "no")
dat$pseudotime <- dpt_vals[rownames(dat)]
colnames(dat)[1:2] <- c("FA1", "FA2") # name columns for clarity
library(ggplot2)
ggplot(dat, aes(x = FA1, y = FA2)) +
# Background cells
geom_point(data = subset(dat, highlight == "no"),
color = "grey85", size = 0.1) +
# Highlighted cells colored by pseudotime
geom_point(data = subset(dat, highlight == "yes"),
aes(color = pseudotime), size = 0.25) +
scale_color_viridis_c(option = "turbo", direction = -1) +
ggtitle(title) +
theme_void() +
theme(
legend.position = "none",
panel.border = element_blank(), # removes any panel border
plot.background = element_rect(fill = "transparent", color = NA),
panel.background = element_rect(fill = "transparent", color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
)
}
Fig 3h
Idents(clones) <- 'Clone_Dynamics'
my_pats <- c('Pattern_1','Pattern_2','Pattern_5','Unknown_1','Pattern_3','Pattern_4')
plots <- lapply(my_pats, function(pat) {
tit <- gsub(pat, '_', ' ')
plotCloneDynDpt(pat, clones, title = tit)
})
cowplot::plot_grid(plotlist = plots, ncol=6)

LS0tCnRpdGxlOiAiUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZyBub3RlYm9vayIKYXV0aG9yOiAiQWJiaWUgSXJlbGFuZCwgRGFycmVuIFR5c29uIgpkYXRlOiAiMjAyNS0wNi0yNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIE1vZGlmeWluZyBvcmlnaW5hbCBjb2RlCk9yaWdpbmFsIGZpbGU6IFtGaWczXzRfNl9FeHRGaWc1LTYtMTBfUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZy5SXShodHRwczovL2dpdGh1Yi5jb20vVEdPbGl2ZXItbGFiL0lyZWxhbmRfQmFzYWxfU0NMQ18yMDI1L2Jsb2IvbWFpbi9SX0NvZGUvRmlnM180XzZfRXh0RmlnNS02LTEwX1JQTV9SUE1BX0FsbG9zX0NlbGxUYWcuUikgIAoKT25seSBDZWxsVGFnIGFuYWx5c2lzIGluY2x1ZGVkIGluIHRoaXMgbm90ZWJvb2suCgojIyMgUmVsYXRlZCB0bzoKKiBGaWcgM2MtaAoqIEV4dGVuZGVkIERhdGEgRmlnIDZiLGMKKiBFeHRlbmRlZCBEYXRhIEZpZyA3ZgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgICBsaWJyYXJ5KFNldXJhdCkKICAgIGxpYnJhcnkoU2V1cmF0T2JqZWN0KQogICAgbGlicmFyeShTdW1tYXJpemVkRXhwZXJpbWVudCkKICAgIGxpYnJhcnkoZ2dwbG90MikKICAgIGxpYnJhcnkoZ2dwdWJyKQogICAgbGlicmFyeSh0aWR5cikKICAgIGxpYnJhcnkocGF0Y2h3b3JrKQogICAgbGlicmFyeSh2aXJpZGlzKQp9KQpgYGAKYGBge3J9ClNBVkVGSUdTIDwtIEZBTFNFCmBgYAoKCmBgYHtyfQpUQk9fc2V1cmF0PC1yZWFkUkRTKCIuLi9kYXRhLzA1XzIwMjVfUlBNX1JQTUFfVEJPX0NlbGxUYWdfU2V1cmF0X3dTaWdzX0ZBX2RwdF9maW5hbC5yZHMiKQpUQk9fc2V1cmF0CmBgYAoKYGBge3J9CmNsb25lcyA8LSByZWFkUkRTKCIuLi9kYXRhLzA1XzIwMjVfUlBNX1JQTUFfVEJPQWxsb19DZWxsVGFnQ2xvbmVzX09ubHljbG9uZXMucmRzIikKYGBgCgpgYGB7cn0KbXlfY29sb3JzIDwtIGMoCiAgIiNFNDFBMUMiLCAjIHN0cm9uZyByZWQKICAiIzM3N0VCOCIsICMgbWVkaXVtIGJsdWUKICAiIzREQUY0QSIsICMgZ3JlZW4KICAiIzk4NEVBMyIsICMgcHVycGxlCiAgIiNGRjdGMDAiLCAjIG9yYW5nZQogICIjRkZGRjMzIiwgIyB5ZWxsb3cKICAiI0E2NTYyOCIsICMgYnJvd24KICAiI2U3Mjk4YSIsICMgcGluawogICIjNjY2NjY2IiwgIyBncmV5CiAgIiM2NkMyQTUiLCAjIHRlYWwKICAiI0ZDOEQ2MiIsICMgc2FsbW9uCiAgIiM4REEwQ0IiLCAjIHNvZnQgYmx1ZQogICIjRTc4QUMzIiwgIyBzb2Z0IHBpbmsgKGRpZmZlcmVudCBmcm9tIDgpCiAgIiNBNkQ4NTQiLCAjIGxpZ2h0IGdyZWVuIChidXQgeWVsbG93aXNoIHRpbnQsIG5vdCBncmVlbikKICAiI0ZGRDkyRiIsICMgbGVtb24geWVsbG93CiAgIiNFNUM0OTQiLCAjIGxpZ2h0IGJyb3duCiAgIiNCM0IzQjMiLCAjIGxpZ2h0IGdyZXkKICAiIzFCOUU3NyIsICMgZGVlcCB0ZWFsCiAgIiNEOTVGMDIiLCAjIGRhcmsgb3JhbmdlCiAgIiM3NTcwQjMiLCAjIHN0cm9uZyBwdXJwbGUKICAiIzY2QTYxRSIgICMgb2xpdmUgZ3JlZW4gKE5PVCBzYW1lIGdyZWVuIGFzIGJlZm9yZSkKKQoKcGhlbm9fY29sIDwtIGMoImJyb3duMiIsImRhcmtvcmNoaWQ0IiwiZG9kZ2VyYmx1ZSIsIiM2NkE2MUUiLCJvcmFuZ2UiLCJ0dXJxdW9pc2U0IiwidHVycXVvaXNlIikKbmFtZXMocGhlbm9fY29sKSA8LSBjKCJORSIsIk5FL05ldXJvbmFsIiwiTmV1cm9uYWwiLCJBVE9IMSIsIlR1ZnQiLCJUcmlwbGUtTmVnIiwiQmFzYWwiKQpgYGAKCiMjIyMgRmlnIDNjCkZvcmNlQXRsYXMgZW1iZWRkaW5nIG9mIFJQTSBhbmQgUlBNQSBDZWxsVGFnZ2VkIGFsbG9ncmFmdGVkIHR1bW9yIGNlbGxzCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9J2xlaWRlbl9zY1ZJXzEuMicsIGNvbHM9bXlfY29sb3JzLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZT00KSAmIAogICAgTm9BeGVzKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSAjIHN1cHByZXNzIGxlZ2VuZAoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9J1BoZW5vJywgY29scz1waGVub19jb2wsIHJlZHVjdGlvbj0nZmEnLCBsYWJlbD1GQUxTRSwgbGFiZWwuc2l6ZT02KSAmIAogICAgTm9BeGVzKCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEV4dHJhY3QgY29vcmRpbmF0ZXMgZnJvbSBmb3JjZS1kaXJlY3RlZCBsYXlvdXQgKG9yIGFueSBsYXlvdXQpCmRhdCA8LSBFbWJlZGRpbmdzKFRCT19zZXVyYXQsIHJlZHVjdGlvbiA9ICJmYSIpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKENsdXN0ZXIgPSBUQk9fc2V1cmF0JGxlaWRlbl9zY1ZJXzEuMiwKICAgICAgICAgR2Vub3R5cGUgPSBUQk9fc2V1cmF0JEdlbm90eXBlKQoKIyBTZXQgdXAgeW91ciBjb2xvciB2ZWN0b3IgKG5hbWVkKQpteV9jb2xvcnNfbmFtZWQgPC0gc2V0TmFtZXMobXlfY29sb3JzLCBsZXZlbHMoVEJPX3NldXJhdCRsZWlkZW5fc2NWSV8xLjIpKQoKIyBQbG90IGVhY2ggR2Vub3R5cGUgd2l0aCBiYWNrZ3JvdW5kIGNlbGxzIGdyZXllZApwbG90cyA8LSBsYXBwbHkodW5pcXVlKGRhdCRHZW5vdHlwZSksIGZ1bmN0aW9uKGcpIHsKICBkYXQkaGlnaGxpZ2h0IDwtIGlmZWxzZShkYXQkR2Vub3R5cGUgPT0gZywgImhpZ2hsaWdodCIsICJiYWNrZ3JvdW5kIikKICBkYXQkQ29sb3IgPC0gaWZlbHNlKGRhdCRHZW5vdHlwZSA9PSBnLCBteV9jb2xvcnNfbmFtZWRbZGF0JENsdXN0ZXJdLCAibGlnaHRncmV5IikKCiAgZ2dwbG90KGRhdCwgYWVzKHggPSBGQV8xLCB5ID0gRkFfMikpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ29sb3IpLCBzaXplID0gMC41KSArCiAgICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICAgIGdndGl0bGUoZykgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIHRoZW1lKAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpCiAgICApCn0pCgojIENvbWJpbmUgaW50byBvbmUgZmlndXJlCmEgPC0gd3JhcF9wbG90cyhwbG90cywgbmNvbCA9IDIpCmEKCmlmKFNBVkVGSUdTKSBnZ3NhdmUoIkZBYnlHZW5vLnBuZyIsIHBsb3Q9IGEsIHdpZHRoPTYsIGhlaWdodD0zLCBkcGk9MzAwLCBiZyA9ICJ0cmFuc3BhcmVudCIpCmBgYAoKCgojIyMgVmlzdWFsaXppbmcgQ2VsbHRhZy9jbG9uZSBkYXRhCgpGcm9tIHRhYmxlIGFib3ZlLCBhZGRlZCB3aGV0aGVyIGNsb25lcyB3ZXJlIHJvYnVzdCBvciBub3QgIApSb2J1c3QgZGVmaW5lZCBhcyA+NSBjZWxscyBwZXIgY2xvbmUgcG9zdC1RQy4gIApDZWxsVGFnIG1ldGFkYXRhOiBjbG9uZSBpbmZvcm1hdGlvbiBjYW4gYWxzbyBiZSBmb3VuZCBpbiBTdXBwbGVtZW50YXJ5IFRhYmxlIDQgb2YgSXJlbGFuZCBldCBhbCwgMjAyNSAgCmBgYHtyfQpJZGVudHMoVEJPX3NldXJhdCkgPC0gJ1JvYnVzdCcKYGBgCgoKYGBge3J9CnRhYmxlKFRCT19zZXVyYXRAbWV0YS5kYXRhJFJvYnVzdCwgVEJPX3NldXJhdEBtZXRhLmRhdGEkVW5JRCkKdGFibGUoVEJPX3NldXJhdEBtZXRhLmRhdGEkUm9idXN0LCBUQk9fc2V1cmF0QG1ldGEuZGF0YSRHZW5vQ1QpCmBgYAojIyMgSWRlbnRpZnkgcm9idXN0IGNsb25lcwpgYGB7cn0KdGFibGUoY2xvbmVzQG1ldGEuZGF0YSRSb2J1c3QsIGNsb25lc0BtZXRhLmRhdGEkR2Vub3R5cGUpCmBgYAoKIyMjIEFzc2VzcyBjbG9uZXMgYnkgbGVpZGVuIGNsdXN0ZXIKIyMjIyBGaWcuIDNkICAKRmlyc3QsIGp1c3QgbG9vayBhdCBiYXIgZ3JhcGgsIG5vIHBhcnRpY3VsYXIgb3JkZXIgIwoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CklkZW50cyhjbG9uZXMpIDwtICdsZWlkZW5fc2NWSV8xLjInCgp4IDwtIHRhYmxlKGNsb25lc0BtZXRhLmRhdGEkQ2VsbFRhZ19DbG9uZSxJZGVudHMoY2xvbmVzKSkKcHJvcG9ydGlvbnMgPC0gYXMuZGF0YS5mcmFtZSgxMDAqcHJvcC50YWJsZSh4LCBtYXJnaW4gPSAxKSkKCiMgcHJvcG9ydGlvbnMkQ2x1c3Rlcgpjb2xuYW1lcyhwcm9wb3J0aW9ucyk8LWMoIkNsdXN0ZXIiLCAiU2FtcGxlIiwgIkZyZXF1ZW5jeSIpCgojIFN0YWNrZWQKcCA8LSBnZ3Bsb3QocHJvcG9ydGlvbnMsIGFlcyhmaWxsPVNhbXBsZSwgeT1GcmVxdWVuY3ksIHg9Q2x1c3RlcikpICsgCiAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iaWRlbnRpdHkiKQoKcCArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teV9jb2xvcnMpICsgCiAgdGhlbWVfYncoKSsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwgYXhpcy50aXRsZS54ID1lbGVtZW50X3RleHQoc2l6ZT0xNCksIAogICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSkrcm90YXRlX3hfdGV4dChzaXplPTcsYW5nbGUgPSA5MCkKCmRhdCA8LSBwJGRhdGEKYGBgClRyYW5zZm9ybSBkYXRhIHRvIHBlcmZvcm0gaGllcmFyY2hpY2FsIChhZ2dsb21lcmF0aXZlKSBjbHVzdGVyaW5nCmBgYHtyfQojIFBpdm90IHRvIHdpZGUgZm9ybWF0OiBDbHVzdGVyIMOXIFNhbXBsZQptYXQgPC0gZGF0ICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBTYW1wbGUsIHZhbHVlc19mcm9tID0gRnJlcXVlbmN5LCB2YWx1ZXNfZmlsbCA9IDApICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJDbHVzdGVyIikgJT4lCiAgYXMubWF0cml4KCkKCm1hdF9ub3JtIDwtIHByb3AudGFibGUobWF0LCBtYXJnaW4gPSAxKSAgIyBub3JtYWxpemUgZWFjaCByb3cgdG8gc3VtIHRvIDEKYGBgCgpIaWVyYXJjaGljYWwgKGFnZ2xvbWVyYXRpdmUpIGNsdXN0ZXJpbmcgb2YgY2xvbmVzIHdpdGggZGVmYXVsdCBwYXJhbWV0ZXJzIGluIGBwaGVhdG1hcCgpYApgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQpobSA8LSBwaGVhdG1hcDo6cGhlYXRtYXAodChtYXQpLCBjdXRyZWVfcm93cyA9IDEsIGN1dHJlZV9jb2xzID0gOCwgY2VsbHdpZHRoID0gNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsaGVpZ2h0ID0gNSwgZm9udHNpemUgPSA4LAogICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmt0dXJxdW9pc2UiLCJibGFjayIsInJlZDIiKSkoMzApKQoKY2xvbmVfb3JkZXIgPC0gY29sbmFtZXModChtYXQpKVtobSR0cmVlX2NvbCRvcmRlcl0KYGBgCgoKIyMjIyBGaWcgM2QKQ2x1c3RlcmVkIGNsb25lcyBieSBwcm9wb3J0aW9ucyBvZiBjZWxscyBpbiBMZWlkZW4gY2x1c3RlcnMKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9CiMgTm93LCBwbG90IGNsb25lcyBieSBMZWlkZW4sIG9yZGVyZWQsIGZpbmFsICMgKEZpZy4gM2QpCklkZW50cyhjbG9uZXMpPC0nbGVpZGVuX3NjVklfMS4yJwoKeCA8LSB0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENlbGxUYWdfQ2xvbmUsSWRlbnRzKGNsb25lcykpCnByb3BvcnRpb25zIDwtIGFzLmRhdGEuZnJhbWUoMTAwKnByb3AudGFibGUoeCwgbWFyZ2luID0gMSkpCgpjb2xuYW1lcyhwcm9wb3J0aW9ucykgPC0gYygiQ2x1c3RlciIsICJTYW1wbGUiLCAiRnJlcXVlbmN5IikKcHJvcG9ydGlvbnMkQ2x1c3RlciA8LSBmYWN0b3IocHJvcG9ydGlvbnMkQ2x1c3RlciwgY2xvbmVfb3JkZXIpCgojIFN0YWNrZWQKcCA8LSBnZ3Bsb3QocHJvcG9ydGlvbnMsIGFlcyhmaWxsPVNhbXBsZSwgeT1GcmVxdWVuY3ksIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0ic3RhY2siLCBzdGF0PSJpZGVudGl0eSIpCgpwICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15X2NvbG9ycykgKyAKICB0aGVtZV9idygpICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksIGF4aXMudGl0bGUueCA9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCAKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTgpLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSkrcm90YXRlX3hfdGV4dChzaXplPTcsYW5nbGUgPSA5MCkgKyAKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiJSBvZiBjbG9uZSIpCgpgYGAKCmBgYHtyfQpJZGVudHMoY2xvbmVzKSA8LSAnQ2xvbmVfRHluYW1pY3MnCnRhYmxlKGNsb25lc0BtZXRhLmRhdGEkQ2xvbmVfRHluYW1pY3MpCmBgYAoKIyMjIEZ1bmN0aW9uIGZvciBwbG90dGluZyBjbG9uZSBkeW5hbWljcwoKYGBge3J9CmNsb25lX3BhdHRlcm5zIDwtIGMoJ1BhdHRlcm5fMScsICdQYXR0ZXJuXzInLCAnUGF0dGVybl8zJywgJ1BhdHRlcm5fNCcsICdQYXR0ZXJuXzUnLCAnVW5rbm93bl8xJywgJ1Vua25vd25fMicpCnBhdHRlcm5fY29sb3JzIDwtIGMoJ29yYW5nZScsICdncmVlbjInLCAncmVkJywgJ3JveWFsYmx1ZTInLCAncHVycGxlJywgJ2dyYXk0MCcsICdibGFjaycpCm5hbWVzKHBhdHRlcm5fY29sb3JzKSA8LSBjbG9uZV9wYXR0ZXJucwpgYGAKCmBgYHtyfQpwbG90Q2xvbmVEeW4gPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgcGF0dGVybl9jb2xvciwgdGl0ID0gIiIpIHsKICAgICMgR2V0IGNlbGxzIG9mIGludGVyZXN0CiAgICBzcyA8LSBzdWJzZXQoY2xvbmVzLCBpZGVudHMgPSBjbG9uZV9wYXR0ZXJuKQogICAgaGlnaGxpZ2h0ZWRfY2VsbHMgPC0gcm93bmFtZXMoc3NAbWV0YS5kYXRhKQogICAgCiAgICAjIFBsb3QgYWxsIGNlbGxzIGluIGdyZXksIGhpZ2hsaWdodCB0aGUgcGF0dGVybiBpbiB5b3VyIGNob3NlbiBjb2xvcgogICAgRGltUGxvdChjbG9uZXMsIAogICAgICAgICAgICByZWR1Y3Rpb24gPSAiZmEiLCAKICAgICAgICAgICAgY2VsbHMuaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0ZWRfY2VsbHMsCiAgICAgICAgICAgIHNpemVzLmhpZ2hsaWdodCA9IDAuMjUsCiAgICAgICAgICAgIGNvbHMuaGlnaGxpZ2h0ID0gcGF0dGVybl9jb2xvciwKICAgICAgICAgICAgY29scyA9ICJncmV5OTAiLCAKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMSkgKyAKICAgIGdndGl0bGUodGl0KSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQp9CmBgYAoKCiMjIyMgRmlnIDNlCkZvcmNlQXRsYXMgZW1iZWRkaW5nIHNob3dpbmcgcmVwcmVzZW50YXRpdmUgY2xvbmVzIG9mIGVhY2ggcGF0dGVybiB3aXRoIGNlbGxzIGNvbG9yZWQgYnkgcGF0dGVybgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjZHAgPC0gKGxhcHBseSgxOjcsIGZ1bmN0aW9uKGkpIHBsb3RDbG9uZUR5bihjbG9uZV9wYXR0ZXJuc1tpXSwgcGF0dGVybl9jb2xvcnNbaV0sIHRpdD1jbG9uZV9wYXR0ZXJuc1tpXSkpKQpwX2NvbWJpbmVkIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IGNkcCwgbmNvbCA9IDQsIG5yb3cgPSAyKQpwX2NvbWJpbmVkCmBgYAoKYGBge3J9CmdldENsb25lQ2VsbElEcyA8LSBmdW5jdGlvbihpZCkgewogICAgY2VsbElEcyA8LSByb3duYW1lcyhzdWJzZXQoY2xvbmVzLGlkZW50cz1jKGlkKSlAbWV0YS5kYXRhKQp9CmNsb25lX2NlbGxfaWRzIDwtIGxhcHBseShzZXROYW1lcyhjbG9uZV9wYXR0ZXJucywgY2xvbmVfcGF0dGVybnMpLCBnZXRDbG9uZUNlbGxJRHMpCmBgYAoKCmBgYHtyfQpwbG90UGhlbm9XaXRoQmFja2dyb3VuZCA8LSBmdW5jdGlvbihjbG9uZV9wYXR0ZXJuLCBwaGVub19jb2wsIHRpdGxlID0gIiIsIHNldXJhdF9vYmogPSBjbG9uZXMpIHsKICAgICMgQ2hlY2sgaWYgJ2ZhJyByZWR1Y3Rpb24gZXhpc3RzCiAgICBpZiAoISJmYSIgJWluJSBuYW1lcyhzZXVyYXRfb2JqQHJlZHVjdGlvbnMpKSB7CiAgICAgICAgc3RvcCgiVGhlICdmYScgcmVkdWN0aW9uIGlzIG5vdCBmb3VuZCBpbiB0aGUgU2V1cmF0IG9iamVjdC4iKQogICAgfQoKICAgICMgRXh0cmFjdCBGQSBjb29yZGluYXRlcyBhbmQgbWV0YWRhdGEKICAgIGNvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gImZhIikpCiAgICBjb29yZHMkY2VsbCA8LSByb3duYW1lcyhjb29yZHMpCgogICAgIyBHZXQgbWV0YWRhdGEKICAgIG1ldGEgPC0gc2V1cmF0X29iakBtZXRhLmRhdGFbLCBjKCJDbG9uZV9EeW5hbWljcyIsICJQaGVubyIpXQogICAgbWV0YSRjZWxsIDwtIHJvd25hbWVzKG1ldGEpCgogICAgIyBNZXJnZSBjb29yZGluYXRlcyBhbmQgbWV0YWRhdGEKICAgIGRhdCA8LSBtZXJnZShjb29yZHMsIG1ldGEsIGJ5ID0gImNlbGwiKQoKICAgICMgRmxhZyBoaWdobGlnaHRlZCBjZWxscwogICAgZGF0JGhpZ2hsaWdodCA8LSBkYXQkQ2xvbmVfRHluYW1pY3MgPT0gY2xvbmVfcGF0dGVybgogICAgZGF0JFBoZW5vIDwtIGRyb3BsZXZlbHMoZGF0JFBoZW5vKQoKICAgICMgU3BsaXQgYmFja2dyb3VuZCBhbmQgZm9yZWdyb3VuZAogICAgYmdfZGF0IDwtIGRhdFshZGF0JGhpZ2hsaWdodCwgXQogICAgZmdfZGF0IDwtIGRhdFtkYXQkaGlnaGxpZ2h0LCBdCgogICAgaWYgKG5yb3coZmdfZGF0KSA9PSAwKSB7CiAgICAgICAgd2FybmluZyhwYXN0ZSgiTm8gY2VsbHMgZm91bmQgZm9yIENsb25lX0R5bmFtaWNzIHBhdHRlcm46IiwgY2xvbmVfcGF0dGVybikpCiAgICAgICAgcmV0dXJuKGdncGxvdCgpICsgZ2d0aXRsZShwYXN0ZShjbG9uZV9wYXR0ZXJuLCAiKG5vIGNlbGxzKSIpKSkKICAgIH0KCiAgICAjIFBsb3QKICAgIHAgPC0gZ2dwbG90KCkgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IGJnX2RhdCwgYWVzKHggPSBGQV8xLCB5ID0gRkFfMiksIGNvbG9yID0gImdyYXk5MCIsIHNpemUgPSAwLjIpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBmZ19kYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIsIGNvbG9yID0gUGhlbm8pLCBzaXplID0gMSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwaGVub19jb2wsIGRyb3AgPSBGQUxTRSkgKwogICAgICAgIGdndGl0bGUodGl0bGUpICsKICAgICAgICB0aGVtZV92b2lkKCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpLAogICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpCiAgICAgICAgKQoKICAgIHJldHVybihwKQp9CmBgYAoKRm9yY2VBdGxhcyBlbWJlZGRpbmcgc2hvd2luZyBjZWxscyBjb2xvcmVkIGJ5IFNDTEMgcGhlbm90eXBlCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTQuNX0KRGltUGxvdChjbG9uZXMsIGdyb3VwLmJ5PSdQaGVubycsIGNvbHM9cGhlbm9fY29sLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9RkFMU0UsIGxhYmVsLnNpemU9NiwgcHQuc2l6ZSA9IDAuMDUpICYgCiAgICBOb0F4ZXMoKQpgYGAKCgojIyMjIEZpZyAzZgpGb3JjZUF0bGFzIGVtYmVkZGluZyBzaG93aW5nIHJlcHJlc2VudGF0aXZlIGNsb25lcyBvZiBlYWNoIHBhdHRlcm4gd2l0aCBjZWxscyBjb2xvcmVkIGJ5IFNDTEMgcGhlbm90eXBlCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBsb3RzIDwtIGxhcHBseShjbG9uZV9wYXR0ZXJucywgZnVuY3Rpb24ocGF0KSBwbG90UGhlbm9XaXRoQmFja2dyb3VuZChwYXQsIHBoZW5vX2NvbCwgdGl0bGUgPSBwYXQpKQpwX2NvbWJpbmVkIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IHBsb3RzLCBuY29sID0gNCwgbnJvdyA9IDIpCnBfY29tYmluZWQKYGBgCgoKIyMjIFZpc3VhbGl6ZSBpbmRpdmlkdWFsIGNsb25lcwpFeHQgRGF0YSBGaWcuIDZiLGMKYGBge3J9CnBsb3RQYXR0ZXJuRHluQnlDbG9uZSA8LSBmdW5jdGlvbihjbG9uZV9wYXR0ZXJuKSB7CiAgICBJZGVudHMoY2xvbmVzKTwtJ0Nsb25lX0R5bmFtaWNzJwogICAgcDEgPC0gc3Vic2V0KGNsb25lcyxpZGVudHM9YyhjbG9uZV9wYXR0ZXJuKSkKICAgIAogICAgcGF0dGVybl9jb2xvciA8LSBwYXR0ZXJuX2NvbG9yc1tjbG9uZV9wYXR0ZXJuXQoKICAgIHRlc3QgPC0gYXMuZGF0YS5mcmFtZShwMSRDZWxsVGFnX0Nsb25lKQogICAgdGVzdCRCYXJjb2RlcyA8LSByb3duYW1lcyh0ZXN0KQogICAgIyBHcm91cCBieSBDZWxsVGFnX0Nsb25lCiAgICB0ZXN0IDwtIHRlc3QgJT4lIGdyb3VwX2J5KHAxJENlbGxUYWdfQ2xvbmUpCiAgICB0ZXN0IDwtIGRwbHlyOjpncm91cF9zcGxpdCh0ZXN0KQogICAgCiAgICBuX2Nsb25lcyA8LSBsZW5ndGgodGVzdCkKICAgICMgUGxvdCBpbiBmb3IgbG9vcCBhbGwgUlBNIGNsb25lcyBpbiBQYXR0ZXJuIDEKICAgIHBsb3RfbHN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IG5fY2xvbmVzKQogICAgZm9yIChpIGluIHNlcShuX2Nsb25lcykpIHsKICAgICAgZyA8LSBEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIG9yZGVyPVRSVUUsIAogICAgICAgICAgICAgICAgICAgY2VsbHMuaGlnaGxpZ2h0PXRlc3RbW2ldXSRCYXJjb2RlcyxzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgICAgICAgICAgICBjb2xzLmhpZ2hsaWdodD1wYXR0ZXJuX2NvbG9yKSArCiAgICAgICAgICBnZ3RpdGxlKHBhc3RlMCh0ZXN0W1tpXV0kYHAxJENlbGxUYWdfQ2xvbmVgWzFdKSkgKyAKICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsZmFjZSA9ICJwbGFpbiIpKSAmIE5vTGVnZW5kKCkgJiAKICAgICAgICAgIE5vQXhlcygpCiAgICAgIHBsb3RfbHN0W1tpXV0gPC0gZwogICAgfQogICAgCiAgICAjIENvbWJpbmUgbXVsdGlwbGUgcGxvdHMgZm9yIG91dHB1dCwgYXMgZGVzaXJlZAogICAgcmV0dXJuKGNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RfbHN0LCBuY29sPTUpKQp9CmBgYAoKYGBge3J9CnBhdHRlcm5fcGxvdF9saXN0IDwtIGxhcHBseShjbG9uZV9wYXR0ZXJucywgcGxvdFBhdHRlcm5EeW5CeUNsb25lKQoKbl9wbG90c19wZXJfcGF0dGVybiA8LSBzYXBwbHkocGF0dGVybl9wbG90X2xpc3QsIGZ1bmN0aW9uKHgpIGxlbmd0aCh4W1snbGF5ZXJzJ11dKSkKCiMjIGNhbGN1bGF0ZSBoZWlnaHQgb2YgZmlndXJlIGJhc2VkIG9uIG51bWJlciBvZiBwbG90cyBwZXIgcGF0dGVybgpwbHRfaGVpZ2h0IDwtIDIuNjY3ICogKChuX3Bsb3RzX3Blcl9wYXR0ZXJuICUvJSA1KSArIDEpCmBgYAoKIyMjIyBFeHQgRGF0YSBGaWcgNmIsYwpgYGB7ciBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1sxXV0KYGBgCgoKYGBge3IgZmlnLmhlaWdodD01LjMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1syXV0KYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTAuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1szXV0KYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTMuMzMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s0XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s1XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTUuMzMsIGZpZy53aWR0aD0xMn0KcGF0dGVybl9wbG90X2xpc3RbWzZdXQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9Mi42NjcsIGZpZy53aWR0aD0xMn0KcGF0dGVybl9wbG90X2xpc3RbWzddXQpgYGAKIyMgVmlzdWFsaXplIG9ubHkgY2xvbmVzIG1hdGNoaW5nIGluIHZpdm8gaW4gRkEgcHJvamVjdGlvbgpFeHQgRGF0YSBGaWcuIDdmCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zfQpJZGVudHMoY2xvbmVzKSA8LSAiQ2VsbFRhZ19DbG9uZSIKIyBTdWJzZXQganVzdCB0aGUgY2xvbmVzIHRoYXQgbWF0Y2ggdGhlIGluIHZpdHJvLCBwYXR0ZXJuIDEKaW52aXRyb21hdGNoIDwtIHN1YnNldChjbG9uZXMsIGlkZW50cz1jKCJSUE1fQ2xvbmVfMTQiLCJSUE1fQ2xvbmVfMiIsIlJQTV9DbG9uZV8zMyIsIlJQTV9DbG9uZV8zNiIsIlJQTV9DbG9uZV82IikpCgpwMV9jZWxscyA8LSBjb2xuYW1lcyhpbnZpdHJvbWF0Y2gpCnAyX2NlbGxzIDwtIGNvbG5hbWVzKHN1YnNldChjbG9uZXMsIGlkZW50cz0iUlBNX0Nsb25lXzIzIikpCnA1X2NlbGxzIDwtIGNvbG5hbWVzKHN1YnNldChjbG9uZXMsIGlkZW50cz0iUlBNX0Nsb25lXzEzIikpCgpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wMV9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PWMoIm9yYW5nZSIpKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9IkNlbGxUYWdfQ2xvbmUiLCByZWR1Y3Rpb249J2ZhJywgCiAgICAgICAgb3JkZXI9VFJVRSwgY2VsbHMuaGlnaGxpZ2h0PXAyX2NlbGxzLCBzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcnNbIlBhdHRlcm5fMiJdKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9IkNlbGxUYWdfQ2xvbmUiLCByZWR1Y3Rpb249J2ZhJywgCiAgICAgICAgb3JkZXI9VFJVRSwgY2VsbHMuaGlnaGxpZ2h0PXA1X2NlbGxzLCBzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcnNbIlBhdHRlcm5fNSJdKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCgoKYGBgCgpgYGB7cn0KCmBgYAoKCiMjIyMgRmlnLiAzZyAKZHB0IHBzdWVkb3RpbWUgaW4gRkEgc3BhY2UgCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMuNzUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkZlYXR1cmVQbG90KFRCT19zZXVyYXQsIGZlYXR1cmVzID0gYygiZHB0X3BzZXVkb3RpbWUiKSwgcHQuc2l6ZT0wLjAxLAogICAgICAgICAgICByZWR1Y3Rpb249J2ZhJywpICsgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb249InZpcmlkaXMiLGRpcmVjdGlvbj0tMSkmIE5vQXhlcygpCgpgYGAKIyMjIyBGdW5jdGlvbiB0byBwbG90IGNsb25lIGR5bmFtaWNzIGNvbG9yZWQgYnkgRFBUCmBgYHtyfQpwbG90Q2xvbmVEeW5EcHQgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgc2V1cmF0X29iaiwgdGl0bGUgPSAiIikgewogICAgIyBTdWJzZXQgY2VsbHMgb2YgaW50ZXJlc3QKICAgIHNzIDwtIHN1YnNldChzZXVyYXRfb2JqLCBpZGVudHMgPSBjbG9uZV9wYXR0ZXJuKQogICAgaGlnaGxpZ2h0X2NlbGxzIDwtIGNvbG5hbWVzKHNzKQoKICAgICMgRXh0cmFjdCBlbWJlZGRpbmdzIGFuZCBtZXRhZGF0YQogICAgZmFfY29vcmRzIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgImZhIikKICAgIGRwdF92YWxzIDwtIHNldXJhdF9vYmpAbWV0YS5kYXRhJGRwdF9wc2V1ZG90aW1lCiAgICBuYW1lcyhkcHRfdmFscykgPC0gcm93bmFtZXMoc2V1cmF0X29iakBtZXRhLmRhdGEpCgogICAgZGF0IDwtIGFzLmRhdGEuZnJhbWUoZmFfY29vcmRzKQogICAgZGF0JGhpZ2hsaWdodCA8LSBpZmVsc2Uocm93bmFtZXMoZGF0KSAlaW4lIGhpZ2hsaWdodF9jZWxscywgInllcyIsICJubyIpCiAgICBkYXQkcHNldWRvdGltZSA8LSBkcHRfdmFsc1tyb3duYW1lcyhkYXQpXQogICAgY29sbmFtZXMoZGF0KVsxOjJdIDwtIGMoIkZBMSIsICJGQTIiKSAgIyBuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IEZBMSwgeSA9IEZBMikpICsKICAgICAgICAjIEJhY2tncm91bmQgY2VsbHMKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGF0LCBoaWdobGlnaHQgPT0gIm5vIiksIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleTg1Iiwgc2l6ZSA9IDAuMSkgKwogICAgICAgICMgSGlnaGxpZ2h0ZWQgY2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdCwgaGlnaGxpZ2h0ID09ICJ5ZXMiKSwgCiAgICAgICAgICAgICAgICAgICBhZXMoY29sb3IgPSBwc2V1ZG90aW1lKSwgc2l6ZSA9IDAuMjUpICsKICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInR1cmJvIiwgZGlyZWN0aW9uID0gLTEpICsKICAgICAgICBnZ3RpdGxlKHRpdGxlKSArCiAgICAgICAgdGhlbWVfdm9pZCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksICAgICAgIyByZW1vdmVzIGFueSBwYW5lbCBib3JkZXIKICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSBOQSksCiAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICApCn0KYGBgCgoKCiMjIyMgRmlnIDNoCmBgYHtyIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpJZGVudHMoY2xvbmVzKSA8LSAnQ2xvbmVfRHluYW1pY3MnCgpteV9wYXRzIDwtIGMoJ1BhdHRlcm5fMScsJ1BhdHRlcm5fMicsJ1BhdHRlcm5fNScsJ1Vua25vd25fMScsJ1BhdHRlcm5fMycsJ1BhdHRlcm5fNCcpCgpwbG90cyA8LSBsYXBwbHkobXlfcGF0cywgZnVuY3Rpb24ocGF0KSB7CiAgICB0aXQgPC0gZ3N1YihwYXQsICdfJywgJyAnKQogICAgcGxvdENsb25lRHluRHB0KHBhdCwgY2xvbmVzLCB0aXRsZSA9IHRpdCkKfSkKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RzLCBuY29sPTYpCmBgYAoK